local Raw = "https://raw.githubusercontent.com/QuantumDreemurr/midi/main/" local HTTPService = game:GetService("HttpService") local Instruments = loadstring(HTTPService:GetAsync(Raw .. "instruments.luau"))() local Parser = loadstring(HTTPService:GetAsync(Raw .. "parse.luau"))() local root = Instance.new("Part") root.Name = "root" root.Anchored = true root.BottomSurface = Enum.SurfaceType.Smooth root.BrickColor = BrickColor.new("Really black") root.CFrame = CFrame.new(66.8749924, 5.75, 44.3125038, 1, 0, 0, 0, 1, 0, 0, 0, 1) root.CanCollide = false root.CanTouch = false root.Color = Color3.fromRGB(17, 17, 17) root.Position = Vector3.new(66.9, 5.75, 44.3) root.Size = Vector3.new(22.6, 11.5, 0.0917) root.TopSurface = Enum.SurfaceType.Smooth root.Parent = script local console = Instance.new("SurfaceGui") console.Name = "console" console.ClipsDescendants = true console.LightInfluence = 1 console.PixelsPerStud = 200 console.SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud console.ZIndexBehavior = Enum.ZIndexBehavior.Sibling local keys = Instance.new("Frame") keys.Name = "keys" keys.BackgroundColor3 = Color3.fromRGB(255, 255, 255) keys.BackgroundTransparency = 1 keys.Position = UDim2.fromScale(0, 0.8) keys.Size = UDim2.fromScale(1, 0.2) local whitekey = Instance.new("Frame") whitekey.Name = "whitekey" whitekey.BackgroundColor3 = Color3.fromRGB(255, 255, 255) whitekey.Size = UDim2.fromScale(0.0192, 1) whitekey.ZIndex = 0 local uIListLayout = Instance.new("UIListLayout") uIListLayout.Name = "UIListLayout" uIListLayout.FillDirection = Enum.FillDirection.Horizontal uIListLayout.SortOrder = Enum.SortOrder.LayoutOrder uIListLayout.Parent = keys local blackkey = Instance.new("Frame") blackkey.Name = "blackkey" blackkey.BackgroundColor3 = Color3.fromRGB(255, 255, 255) blackkey.Size = UDim2.fromScale(0, 1) blackkey.ZIndex = 2 local inner = Instance.new("Frame") inner.Name = "inner" inner.BackgroundColor3 = Color3.fromRGB(0, 0, 0) inner.Position = UDim2.fromOffset(-30, 0) inner.Size = UDim2.fromOffset(60, 270) inner.ZIndex = 0 inner.Parent = blackkey keys.Parent = console --[[local midi_notes = { [50] = "Tom1", [47] = "Tom2", [45] = "Tom3", [49] = "Crash", [51] = "Crash", [52] = "Crash", [55] = "Crash", [57] = "Crash", [42] = "HiHat", [44] = "HiHat", [46] = "HiHat", [36] = "Kick", [59] = "Ride", [38] = "Snare", [40] = "Snare" }]] local Percussion = { -- Bass Drum ['35'] = 'rbxassetid://31173820', ['36'] = 'rbxassetid://31173820', --IDK ['82'] = 'rbxassetid://31173898', --Conga ['62'] = 'rbxassetid://57802212', --high ['63'] = 'rbxassetid://57802212', --high ['64'] = 'rbxassetid://57802134', -- low --Conga ['60'] = 'rbxassetid://57802055', --high ['61'] = 'rbxassetid://57801983', -- low --Toms ['45'] = 'rbxassetid://31173881', -- low ['47'] = 'rbxassetid://31173863', -- lowmid ['48'] = 'rbxassetid://31173863', -- highmid ['50'] = 'rbxassetid://31173844', -- high --Cowbell ['56'] = 'rbxassetid://9120917609', --Cymbals ['49'] = 'rbxassetid://31173771', -- Crash ['51'] = 'rbxassetid://31173898', -- Ride ['57'] = 'rbxassetid://31173771', -- Crash ['59'] = 'rbxassetid://31173898', -- Ride ['42'] = 'rbxassetid://4702649974', --HiHat Closed ['44'] = 'rbxassetid://4702649717', --HiHat ['46'] = 'rbxassetid://4702649315', --HiHat -- Cuica ['78'] = 'rbxassetid://7430849680', ['79'] = 'rbxassetid://7430849680', -- Snare ['38'] = 'rbxassetid://31173799', ['40'] = 'rbxassetid://31173799', --Triangle ['80'] = 'rbxassetid://6732342375', ['81'] = 'rbxassetid://6732342375', -- Wood Blocks ['76']='rbxassetid://9120917978', ['77']='rbxassetid://9120917605', } local PianoLayout = { --0 IS BLACK --1 IS WHITE 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 } local LayoutKeys = {} local LayoutKeyNotes = {} local LayoutSkip = 3 -- Skip the first three when repeating local Keys = {["C"] = 1, ["C#"] = 2, ["D"] = 3, ["D#"] = 4, ["E"] = 5, ["F"] = 6, ["F#"] = 7, ["G"] = 8, ["G#"] = 9, ["A"] = 10, ["A#"] = 11, ["B"] = 12} local PianoKeys = ("C C# D D# E F F# G G# A A# B"):split(" ") for Octave = 1, 7 do for Key = 1, #PianoLayout do if Octave > 1 and Key <= 3 then continue end local Colour = PianoLayout[Key] local NewKeyVisual if Colour == 1 then NewKeyVisual = whitekey:Clone() else NewKeyVisual = blackkey:Clone() end if Key <= 3 and Octave == 1 then Key += 12 end local Index = PianoKeys[Key - 3] .. (Octave == 1 and "" or Octave - 1) LayoutKeys[Index] = NewKeyVisual LayoutKeyNotes[Key + (Octave * 12)] = NewKeyVisual NewKeyVisual.Name = Index NewKeyVisual.Parent = keys end end local FinalKey = whitekey:Clone() FinalKey.Parent = keys console.Parent = root local SoundList = { "233836579", --C/C# "233844049", --D/D# "233845680", --E/F "233852841", --F#/G "233854135", --G#/A "233856105", --A#/B } -- Each note takes up exactly 8 seconds in audio. i.e C2 lasts 8 secs, C2# lasts 8 secs, C3 lasts 8 secs, C3# lasts 8 secs etc. for each audio -- These are the IDs of the piano sounds. local locked = false task.defer(function() while true do task.wait() if not locked then root.CFrame = owner.Character.HumanoidRootPart.CFrame * CFrame.new(0,3, -6) * CFrame.Angles(0,math.pi,0) end end end) local Base64 = {} do local b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" function Base64.encode(data) return ((data:gsub('.', function(x) local r,b='',x:byte() for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end return r; end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) if (#x < 6) then return '' end local c=0 for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6 - i) or 0) end return b:sub(c + 1,c + 1) end)..({ '', '==', '=' })[#data % 3+1]) end function Base64.decode(data) data = string.gsub(data, '[^'..b..'=]', '') return (data:gsub('.', function(x) if (x == '=') then return '' end local r,f='',(b:find(x)-1) for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end return r; end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) if (#x ~= 8) then return '' end local c=0 for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end return string.char(c) end)) end end local nbs = {} local MIDI = _G.MIDI_DATA if not MIDI_DATA then do MIDI = {} local API = "https://api.github.com/repos/QuantumDreemurr/midi/contents/files" local JSON = HTTPService:JSONDecode(HTTPService:GetAsync(API)) for i = 1, #JSON do local Data = JSON[i] MIDI[Data.name] = Data.download_url end _G.MIDI_DATA = MIDI end end do local MIDICache = {} function DecodeMIDI(URL) print(URL) local Data = (MIDICache[URL] or HTTPService:GetAsync(URL)) if not MIDICache[URL] then MIDICache[URL] = Data end Data = Parser:DecompressDeflate(Base64.decode(Data)) return HTTPService:JSONDecode(Data) end end local visual = Instance.new("Frame") visual.BackgroundTransparency = 1 visual.Size = UDim2.fromScale(1,1) visual.Parent = console function SearchInstrument(inst: number) for Index = 1, #Instruments do local Data = Instruments[Index] if Data[2] == inst then return Data end end end function PitchRange(note, offset) return (440 / 32) * math.pow(2, ((note + (offset or 0)) / 12)) / 440 end local SlowedAndReverb = false -- 💀 local PitchFactor = 0.6 local SFXClone = Instance.new("Sound") _G.SoundPool = {} _G.KeyPool = {} _G.ParticlePool = {} function PianoSound(note, oct) local note2 = (note - 1)%12 + 1 -- Which note? (1-12) local octave = math.ceil(note/12) + oct -- Which octave? local sound = math.ceil(note2/2) -- Which audio? local offset = 16 * (octave - 1) + 8 * (1 - note2%2) -- How far in audio? local audio = SoundPool[1] or SFXClone:Clone() -- Create the audio audio.SoundId = "rbxassetid://"..SoundList[sound] -- Give its sound audio.TimePosition = offset + (octave - .9)/15 -- set the time position return audio end keys.ZIndex = 2 local Threads = {} local Position = owner.Character.Head.Position + Vector3.new(0,30,0) local ID = 0 local Visuals = {} local IsPlaying = false local Vol = 0 local Crunch = false local MagicNumber = 1/(88 - 36) function PlaySong(Name) ID += 1 for i,v in Threads do task.cancel(v) end local CurrentSong = DecodeMIDI(MIDI[Name]) local Speed = 1 local LoopSound = false local UseDuration = false local OctaveOffset = 0 SFXClone.Looped = LoopSound if SlowedAndReverb then Speed = 0.6 PitchFactor *= 0.7 game:GetService("SoundService").AmbientReverb = "ConcertHall" end for i in Visuals do table.insert(_G.KeyPool, i) i.Parent = nil end table.clear(Visuals) local Current = ID while ID == Current do local Length = 0 for TrackNumber = 1, #CurrentSong.tracks do local Track = CurrentSong.tracks[TrackNumber] local InstrumentData = Track.instrument for NoteNumber = 1, #Track.notes do local Note = Track.notes[NoteNumber] local Instrument = SearchInstrument(InstrumentData.name) or Instruments[1] local ID = Instrument[1] if Instrument then local Time = Note.time / Speed if Time > Length then Length = Time end local Duration = Note.duration local IsPercussion = false if Percussion[tostring(Note.midi)] and InstrumentData.family == "drums" then IsPercussion = true ID = Percussion[tostring(Note.midi)] end if ID == "piano" then ID = "rbxassetid://176283853" Duration *= 1.25 end local Settings = Instrument.settings local Num = Note.midi + (Settings and Settings.offset or 0) table.insert(Threads, task.delay(Time, function() -- Wait for note to come in -- Create Visual local keyvisual local KeyNumber = tonumber(Note.name:sub(#Note.name,#Note.name)) local NoteKey = (KeyNumber and Note.name:sub(1,-2) or Note.name) local Key = LayoutKeys[Note.name] if #visual:GetChildren() < 60 and Key then keyvisual = _G.KeyPool[1] or Instance.new("Frame") table.remove(_G.KeyPool, 1) keyvisual.Name = "keyvisual" keyvisual.BackgroundColor3 = Color3.fromHSV(TrackNumber / #CurrentSong.tracks, 1, 1) keyvisual.BorderSizePixel = 0 keyvisual.Position = UDim2.new(0, Key.AbsolutePosition.X + (NoteKey:find("#") and -30 or 0), 0,0) keyvisual.Size = NoteKey:find("#") and UDim2.new(0, 60, 0, Duration * 400) or UDim2.new(MagicNumber, 0, 0, Duration * 400) keyvisual.Parent = visual Visuals[keyvisual] = true game:GetService("TweenService"):Create(keyvisual, TweenInfo.new(2, Enum.EasingStyle.Linear), {Position = UDim2.new(0,Key.AbsolutePosition.X + (NoteKey:find("#") and -30 or 0), 0.8,0)}):Play() end task.wait(2) -- Wait for visual to end -- Play Sounds if IsPlaying then return end if keyvisual then keyvisual.Parent = nil Visuals[keyvisual] = nil local Coloured = Key:FindFirstChild("inner") or Key Coloured.BackgroundColor3 = keyvisual.BackgroundColor3 game:GetService("TweenService"):Create(Coloured, TweenInfo.new(math.max(Duration, 0.35)), {BackgroundColor3 = NoteKey:find("#") and Color3.new() or Color3.new(1,1,1)}):Play() table.insert(_G.KeyPool, keyvisual) end local SFX = (_G.SoundPool[1] or SFXClone:Clone()) SFX.SoundId = (Crunch and "rbxassetid://3494283437" or ID) if not IsPercussion then SFX.PlaybackSpeed = PitchRange(Num, OctaveOffset) * PitchFactor task.delay(math.max(Duration, 0.2), function() SFX:Stop() table.insert(_G.SoundPool, SFX) end) end table.remove(_G.SoundPool, 1) SFX.TimePosition += 0.05 SFX.Volume = Note.velocity + Vol SFX.Parent = root if Settings and Settings.Loop then SFX.Looped = true else SFX.Looped = false end SFX:Play() -- Noteblocks if nbs[(Note.midi-1)%6+1] then local Col = Color3.fromHSV((Note.midi%24)/24, 1, 1) local newNote if Col == nbs[(Note.midi-1)%6+1].Attachment.NewNote.Color.Keypoints[1].Value then newNote = nbs[(Note.midi-1)%6+1].Attachment.NewNote else newNote = _G.ParticlePool[1] or Instance.new("ParticleEmitter") newNote.Name = "NewNote" newNote.Drag = 16 newNote.Enabled = false newNote.Lifetime = NumberRange.new(1) newNote.LightInfluence = 1 newNote.Size = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.85), NumberSequenceKeypoint.new(1, 0.85), }) newNote.Speed = NumberRange.new(30) newNote.Texture = "rbxassetid://6987871643" newNote.Parent = nbs[(Note.midi-1)%6+1].Attachment table.remove(_G.ParticlePool, 1) task.delay(1, function() table.insert(_G.ParticlePool, newNote) end) end newNote.Color = ColorSequence.new(Col) newNote:Emit(1) end end)) end end end task.wait(Length) end end owner.Chatted:Connect(function(m) if m == "!updateMIDI" then print("Updating...") do MIDI = {} local API = "https://api.github.com/repos/QuantumDreemurr/midi/contents/files" local JSON = HTTPService:JSONDecode(HTTPService:GetAsync(API)) for i = 1, #JSON do local Data = JSON[i] print(Data.name) MIDI[Data.name] = Data.download_url end _G.MIDI_DATA = MIDI end print("Done!") return end if m:sub(1,2) == "!v" then Vol = tonumber(m : sub(4)) end if m == "!lockVisual" then locked = not locked return end if m == "!getMIDI" then for Name in MIDI do print(Name) end return end if m == "!noteblock" then Crunch = not Crunch end if m == "!noteblocks" then local X, Z = owner.Character.Head.Position.X, owner.Character.Head.Position.Z for i = 1, 6 do local noteBlock = Instance.new("Part", script) noteBlock.Name = "NoteBlock" noteBlock.Anchored = true noteBlock.BottomSurface = Enum.SurfaceType.Smooth noteBlock.CFrame = CFrame.new(X + (3 * i), 2, Z, 1, 0, 0, 0, 1, 0, 0, 0, 1) noteBlock.Size = Vector3.new(3, 3, 3) noteBlock.TopSurface = Enum.SurfaceType.Smooth local mesh = Instance.new("SpecialMesh") mesh.Name = "Mesh" mesh.MeshType = Enum.MeshType.FileMesh mesh.TextureId = "http://www.roblox.com/asset/?id=59262397" mesh.Scale = Vector3.new(3, 3, 3) mesh.Parent = noteBlock local attachment = Instance.new("Attachment") attachment.Name = "Attachment" attachment.CFrame = CFrame.new(0, 1.5, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1) local newNote = Instance.new("ParticleEmitter") newNote.Name = "NewNote" newNote.Drag = 16 newNote.Enabled = false newNote.Lifetime = NumberRange.new(1) newNote.LightInfluence = 1 newNote.Size = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.85), NumberSequenceKeypoint.new(1, 0.85), }) newNote.Speed = NumberRange.new(30) newNote.Texture = "rbxassetid://6987871643" newNote.Parent = attachment attachment.Parent = noteBlock nbs[i] = noteBlock end end if m:sub(1,2) == "!p" then PlaySong(m:sub(4)) end end)